package com.allenkerr;

import com.alibaba.fastjson.JSON;
import com.allenkerr.model.Article;
import com.allenkerr.vo.ArticleResult;
import com.mongodb.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.*;

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

/**
 * Created by 小柯 on 17/6/27.
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoDbTest02 {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 获取所有 collection 的名字
     */
    @Test
    public void test1() {
        Set<String> collectionNames = mongoTemplate.getCollectionNames();
        collectionNames.forEach(System.out::println);
    }

    /**
     * 添加数据
     */
    @Test
    public void test2() {
        //10条数据
        for (int i = 0; i < 10; i++) {
            Article article = new Article();
            article.setTitle("mongoTemplate的使用" + i);
            article.setAuthor("小柯");
            article.setUrl("http://xiaoke.com/post/" + i);
            article.setTags(Arrays.asList("教程", "mongo", "java"));
            article.setAddTime(new Date());
            article.setVisitCount(Long.valueOf(new Random().nextInt(100)));
            //逐个保存
            mongoTemplate.insert(article);
        }

        /*List<Article> articles = new ArrayList<>(10);
        //10条数据
        for (int i = 0; i < 10; i++) {
            Article article = new Article();
            article.setTitle("MongoDB教程" + i);
            article.setAuthor("Allen Kerr");
            article.setUrl("http://xiaoke.com/post/" + i);
            article.setTags(Arrays.asList("教程", "mongo"));
            article.setAddTime(new Date());
            article.setVisitCount(Long.valueOf(new Random().nextInt(100)));
            //添加到集合中
            articles.add(article);
        }
        //批量添加
        mongoTemplate.insertAll(articles);*/
    }

    /**
     * 删除collection
     */
    @Test
    public void test3() {
        mongoTemplate.dropCollection(Article.class);
    }

    /**
     * 查询
     */
    @Test
    public void test4() {
        //查所有
        List<Article> all = mongoTemplate.findAll(Article.class);
        all.forEach(System.out::println);
        System.out.println("---------------------------------\n");

        //查几条数据
        Query query = Query.query(Criteria.where("author").is("小柯"));
        List<Article> articles = mongoTemplate.find(query, Article.class);
        articles.forEach(System.out::println);
        System.out.println("---------------------------------\n");

        //查一条数据
        Query query1 = Query.query(Criteria.where("author").is("Allen Kerr"));
        Article article = mongoTemplate.findOne(query1, Article.class);
        System.out.println(article);
        System.out.println("---------------------------------\n");

        //聚合
        long count = mongoTemplate.count(query, Article.class);
        System.out.println("count = " + count);
        System.out.println("---------------------------------\n");

        //in查询
        List<String> authors = Arrays.asList("小柯", "Allen Kerr");
        Query query2 = Query.query(Criteria.where("author").in(authors));
        List<Article> articles1 = mongoTemplate.find(query2, Article.class);
        articles1.forEach(System.out::println);
        System.out.println("---------------------------------\n");

        //ne查询，即不等于查询
        Query query3 = Query.query(Criteria.where("author").ne("小柯"));
        List<Article> articles2 = mongoTemplate.find(query3, Article.class);
        articles2.forEach(System.out::println);
        System.out.println("---------------------------------\n");

        //查询访问量小于10的文章，使用 lt
        System.out.println("访问量小于10：");
        Query query4 = Query.query(Criteria.where("visitCount").lt(10));
        List<Article> articles3 = mongoTemplate.find(query4, Article.class);
        articles3.forEach(System.out::println);
        System.out.println("---------------------------------\n");

        //访问量大于5，小于10
        System.out.println("访问量大于5，小于10：");
        Query query5 = Query.query(Criteria.where("visitCount").lt(10).gt(5));
        List<Article> articles4 = mongoTemplate.find(query5, Article.class);
        articles4.forEach(System.out::println);
        System.out.println("---------------------------------\n");

        //数组查询
        Query query6 = Query.query(Criteria.where("tags").size(3));
        List<Article> articles5 = mongoTemplate.find(query6, Article.class);
        articles5.forEach(System.out::println);
        System.out.println("---------------------------------");

        //or查询，作者是小柯或者访问量为40的文章
        Query query7 = Query.query(Criteria.where("").orOperator(
                Criteria.where("author").is("小柯"),
                Criteria.where("visitCount").is(40)
        ));
        List<Article> articles6 = mongoTemplate.find(query7, Article.class);
        articles6.forEach(System.out::println);
        System.out.println("---------------------------------");
    }

    @Test
    public void test4_4() {
        Criteria[] criterias = new Criteria[2];
        criterias[0] = Criteria.where("author").is("小柯");
        criterias[1] = Criteria.where("visitCount").is(40);
//        criterias[2] = null;
        Query query7 = Query.query(Criteria.where("").orOperator(
                criterias
        ));
        /*Query query7 = Query.query(Criteria.where("").andOperator(
                Criteria.where("author").is("小柯"),
                Criteria.where("visitCount").is(40),
                null
        ));*/
        List<Article> articles6 = mongoTemplate.find(query7, Article.class);
        articles6.forEach(System.out::println);
        System.out.println("---------------------------------");
    }

    @Test
    public void test4_1() {
        Query query = new Query();
        //排序
        query.with(new Sort(Sort.Direction.DESC, "addTime"));
        //指定字段
        query.fields().include("author").include("addTime").include("title");
        //分页
        query.skip(2).limit(10);

        List<Article> articles = mongoTemplate.find(query, Article.class);
        articles.forEach(System.out::println);
    }

    @Test
    public void test4_2() {
        Query query = new Query();
        //排序
        query.with(new Sort(Sort.Direction.DESC, "visitCount"));
        //指定字段
        query.fields().include("author").include("addTime").include("title").exclude("_id").include("visitCount");
        //分页
        query.skip(2).limit(10);

        List<Article> articles = mongoTemplate.find(query, Article.class);
        articles.forEach(System.out::println);
    }

    @Test
    public void test4_3() {
        Query query = new Query();
        //指定字段
        query.fields().include("author").include("addTime").include("title").exclude("_id").include("visitCount");

        //查总数
        long count = mongoTemplate.count(query, Article.class);
        System.out.println("总记录数 = " + count);

        //排序 + 分页
        query.with(new PageRequest(2, 5, new Sort(Sort.Direction.DESC, "visitCount")));

        List<Article> articles = mongoTemplate.find(query, Article.class);
        System.out.println();
        articles.forEach(System.out::println);
        System.out.println();
    }

    /**
     * http://www.jianshu.com/p/5b4809324cf9
     * 在data框架中如何使用Aggregation进行分组统计
     * -----------------------------------------
     * $project - 可以从子文档中提取字段，可以重命名字段。
     * $match - 可以实现查找的功能。
     * $limit - 接受一个数字n，返回结果集中的前n个文档。
     * $skip - 接受一个数字n，丢弃结果集中的前n个文档。
     * $group - 统计操作， 还提供了一系列子命令。
     * $avg, $sum 等等函数…。
     * $sort - 排序。
     * -----------------------------------------
     */
    @Test
    public void test5() {
        //需求：统计下每个用户发过多少篇文章
        //$group:   根据author分组，然后统计次数，用$sum函数，显示第一个名称
        //$project: 定义要显示的key,1为显示，0为不显示
        //$match:   过滤掉没发过文章的用户，次数大于0
        /*原始语句
        db.article_info.aggregate([
            {
                "$group": {
                    "_id": "$author",
                    "count": {
                        "$sum": 1
                    },
                    "name": {
                        "$first": "$author"
                    }
                }
            },
            {
                "$project": {
                    "name": 1,
                    "count": 1,
                    "_id": 0
                }
            },
            {
                "$match": {
                    "count": {
                        "$gt": 0
                    }
                }
            }
        ]);
         */

        //java实现
        Aggregation aggregation = newAggregation(
                group("author").count().as("count").first("author").as("name"),
                project("name", "count"),//显示name、count
                sort(Sort.Direction.DESC, "count"),//按照 count 降序排列
                match(Criteria.where("count").gt(0))
        );
        AggregationResults<ArticleResult> results = mongoTemplate.aggregate(aggregation,
                "article_info", ArticleResult.class);
        results.forEach(System.out::println);
    }

    @Test
    public void test6() {
        //需求：统计下每个用户发过多少篇文章
        //使用Java驱动的方式
        List<DBObject> pipeline = new ArrayList<>();

        BasicDBObject group = new BasicDBObject();
        group.put("$group", new BasicDBObject("_id", "$author")
                .append("count", new BasicDBObject("$sum", 1))
                .append("name", new BasicDBObject("$first", "$author")));

        BasicDBObject project = new BasicDBObject();
        project.put("$project", new BasicDBObject("name", 1)//name显示
                .append("count", 1)//count显示
                .append("_id", 0)//id不显示
        );

        pipeline.add(group);
        pipeline.add(project);

        AggregationOutput output = mongoTemplate.getCollection("article_info").aggregate(pipeline);
        Iterable<DBObject> results = output.results();
        results.forEach(System.out::println);
    }

    /**
     * 修改数据
     */
    @Test
    public void test7() {
        Query query = Query.query(Criteria.where("author").is("小柯"));
        Update update = Update.update("author", "小柯柯");
        WriteResult writeResult = mongoTemplate.updateFirst(query, update, Article.class);
        System.out.println(JSON.toJSON(writeResult));
    }

    /**
     * 删除数据
     */
    @Test
    public void test8() {
        Query query = Query.query(Criteria.where("author").is("Allen Kerr"));

        //删除所有
        //List<Article> articlesBeRemoved = mongoTemplate.findAllAndRemove(query, Article.class);
        //WriteResult result = mongoTemplate.remove(query, Article.class);

        //删除一条
        Article articleBeRemoved = mongoTemplate.findAndRemove(query, Article.class);

        System.out.println(JSON.toJSONString(articleBeRemoved));
    }

    private static DBCollection col;


    @Before
    public void before() {
        col = mongoTemplate.getCollection("article");
    }

    /**
     * mongodb-driver.jar 操作 mongodb
     */
    @Test
    public void test9() {
        WriteResult result = col.insert(new BasicDBObject("author", "小柯")
                .append("title", "母猪的产后护理"));
        System.out.println(JSON.toJSONString(result));
    }

    @Test
    public void test10() {
        DBObject query = new BasicDBObject("author", "小柯");
        DBObject update = new BasicDBObject("$set", new BasicDBObject("title", "精修挖掘机"));
        col.update(query, update);
    }

    @Test
    public void test11() {
        DBCursor dbObjects = col.find();
        while (dbObjects.hasNext())
            System.out.println(dbObjects.next());
        System.out.println("---------------------------------");
        dbObjects.forEach(System.out::println);
    }

    /**
     * 查询固定的列
     */
    @Test
    public void test12() {
        DBObject query = new BasicDBObject("author", "小柯");
        DBObject fields = new BasicDBObject("author", 1)//author 显示
                .append("_id", 0);//id 不显示
        //DBCursor dbObjects = col.find(query, fields);
        DBCursor dbObjects = col.find(query, fields);
        dbObjects.forEach(System.out::println);
    }

    /**
     * distinct 使用方法一
     * > db.article_info.distinct("author",{"visit_count":25})
     * [ "小柯柯", "Allen Kerr" ]
     * > db.article_info.distinct("author",{"visit_count":25}).length
     * 2
     */
    @Test
    public void test13() {
        DBCollection collection = mongoTemplate.getCollection("article_info");
        DBObject query = new BasicDBObject("visit_count", 25);
        List list = collection.distinct("author", query);

        System.out.println("list.size() = " + list.size());
        list.forEach(System.out::println);
    }

    /**
     * distinct 使用方法二
     * > db.article_info.aggregate([{"$match":{"visit_count":{"$eq":25}}},{"$group":{"_id":"$author"}}])
     * { "_id" : "Allen Kerr" }
     * { "_id" : "小柯柯" }
     */
    @Test
    public void test14() {
        Aggregation aggregation = newAggregation(
                match(Criteria.where("visit_count").is(25)),
                group("author").first("author").as("name")
        );
        AggregationResults<ArticleResult> articleResults
                = mongoTemplate.aggregate(aggregation, "article_info", ArticleResult.class);

        System.out.println("articleResults.size = " + articleResults.getMappedResults().size());
        articleResults.forEach(System.out::println);
    }

    @Test
    public void test15() {
        Aggregation aggregation = newAggregation(
                match(Criteria.where("visit_count").is(25)),
                group("author")
        );
        AggregationResults<Article> articles
                = mongoTemplate.aggregate(aggregation, "article_info", Article.class);

        DBObject rawResults = articles.getRawResults();
        System.out.println("rawResults = " + JSON.toJSONString(rawResults));

        System.out.println("articles.size = " + articles.getMappedResults().size());
        articles.forEach(System.out::println);
    }

}
